#include <stdio.h>
#include "opt.h"
#include "eth.h"
#include "flow.h"
#include "term.h"

#define MAX_FLOW 100

static char           *ifname = NULL;
static flow            flows[MAX_FLOW];
static pthread_mutex_t flow_mutex;
static int             nflows = 0;
static int             rate = 0;
static int             iter_max = 0;
static int             npacket = 0;
static pthread_t       poll_tx_thread;
static int             quit = 0;
static int             curses = 1;
static int             line = 0;


static void 
print_help() 
{



}


static int 
parse_options(int    argc, 
              char  *argv[], 
              char **ifname,   
              flow  *flows,
              int   *nflows,
              int   *rate, 
              int   *iters) 
{
    char *sopt;
    int   soptindex = 1;
    IP    dst_addr; 
    flow *flow;
 
    *ifname = NULL;
    *nflows = 0;

    while (sopt = sgetopt(argc, argv, &soptindex)) {
        if (strcmp(sopt, "ip") == 0) {
            if (soptarg != NULL) {
                dst_addr = inet_addr(soptarg);  
                if (dst_addr == INADDR_NONE) {
                    fprintf(stderr, "ERROR: bad address: %s\n", soptarg);
                    return -1;
                } 
                if (*nflows > MAX_FLOW) {
                    fprintf(stderr, "ERROR: max addresses ( > %d)\n", MAX_FLOW);
                    return -1;
                }
                flow = &flows[(*nflows)++];
                flow->dip = dst_addr;
            } else {
                fprintf(stderr, "ERROR: missing address after -ip option\n");
                return -1;
            }
        } else if (strcmp(sopt, "if") == 0) {
            if (soptarg != NULL) {
                *ifname = soptarg;
            } else {
                fprintf(stderr, "ERROR: missing interface after -if option\n");
                return -1;
            }
        } else if (strcmp(sopt, "rate") == 0) {
            if (soptarg != NULL) {
                if (soptarg[0] == '0' && strlen(soptarg) == 1) {
                    fprintf(stderr, "INFO: max rate implied (rate == 0)\n");
                    *rate = 0;
                } else {
                    *rate = atoi(soptarg);
                     if (*rate <= 0) {
                         fprintf(stderr, "ERROR: invalid rate: %s\n", soptarg);
                         return -1;
                     }        
                }
            } else {
                fprintf(stderr, "ERROR: missing rate after -rate option\n");
                return -1;
            }
        } else if (strcmp(sopt, "count") == 0) {
            if (soptarg != NULL) {
                    if (soptarg[0] == '0' && strlen(soptarg) == 1) {
                    fprintf(stderr, "INFO: infinite iterations implied " \
                                    "(count == 0)\n");
                    *iters = 0;
                } else {
                    *iters = atoi(soptarg);
                     if (*iters <= 0) {
                         fprintf(stderr, "ERROR: invalid count: %s\n", soptarg);
                         return -1;
                     }
                }
            } else {
                fprintf(stderr, "ERROR: missing interations after -count option\n");
                return -1;
            }
        } else if (strcmp(sopt, "f") == 0) {
            if (soptarg != NULL) { 
                *nflows = parse_flows(soptarg, flows, MAX_FLOW);

                if (*nflows < 0) {
                    return -1;
                } else if (*nflows == 0) {
                    
                }
            } else {
                fprintf(stderr, "ERROR: missing file after -f option\n");
                return -1;
            }
        } else if (strcmp(sopt, "d") == 0) {
            curses = 0;
        } else {
            fprintf(stderr, "WARNING: unrecognized option: %s\n", sopt);
        }
    }

    if (*ifname == NULL) {
        fprintf(stderr, "ERROR: no interface name provided\n");
        return -1;
    }

    if (*nflows == 0) {
        fprintf(stderr, "ERROR: no traffic destinations provided\n");
        return -1;
    }

  
    return 0;
}


static void
print_tx_flows_header(void) 
{
    int   index;
    flow *flow;
    IN    dst;

    printf("\nTx traffic to these %d destinations via %s:\n\n", nflows, ifname);

    for (index = 0; index < nflows; index++) {
        flow = &flows[index];
        dst.s_addr = flow->dip;
        printf("%s\n", inet_ntoa(dst));
    }

    printf("\nTx rates:\n\n");
    fflush(stdout);
}


static void
refresh_tx_flow_rates(void)
{
    int   index;
    flow *flow;
    IN    dst;
    
    line = 0;

    if (curses) {
        tprint(line++, 0, "Tx Traffic to these %d destinations via [%s]", nflows, ifname);
        line++;
        tprint(line,   0, "IP Destination");
        tprint(line,  18, "Tx Rate");
        tprint(line,  30, "Total Tx");
        line++;
        line++;
    }

    for (index = 0; index < nflows; index++, line++) {
        flow = &flows[index];
        dst.s_addr = flow->dip;

        if (curses) {
            attron(A_BOLD);
            tprint(line,  0, "%s ",    inet_ntoa(dst));
            attroff(A_BOLD);
            if (!flow->error) { 
                tprint(line, 18, "%d P/s    ", flow->send);
            } else {
                tprint(line, 18, "(error)   "); 
            }
            tprint(line, 30, "%d Packets", flow->c_send);
        } else {
            if (!flow->error) {
                printf("[%s] %2d P/s   ", inet_ntoa(dst), flow->send);
            } else { 
                printf("[%s] ERROR     ", inet_ntoa(dst));
            }
        }
        
        flow->send = 0;
    }

    if (curses) {
        line++;
        tprint(line,  0, "TOTAL  ");
        tprint(line, 30, "%d Packets    ", npacket);
        line++;
        tprint(line, 0, "");
    } else {
        printf("[TOTAL] %d Packets %s \r", npacket, "      ");
    }
    
    if (curses) {
        refresh(); 
    } else {
        fflush(stdout); 
    }
}


static void 
print_tx_flow_totals(void) 
{
    
}


static void *
poll_tx_flows(void *arg) 
{
    int    index;
    flow  *flow;
    IN     dst;

    /*
     * Give a little time to start the flows before we poll
     */
    sleep(1);

    while (!quit) {
       
        pthread_mutex_lock(&flow_mutex);

        refresh_tx_flow_rates();

        pthread_mutex_unlock(&flow_mutex);

        sleep(1);
    }
}    


static void 
tx_flows(IF *intf) 
{
    int   index;
    flow *flow;
    IN    dst;
    void *packet;
    int   rc;

    for (index = 0; index < nflows; index++) {

        flow = &flows[index]; 
        dst.s_addr = flow->dip;
         
        packet = eth_ip_packet(flow->smac, flow->dmac, &flow->sip, &flow->dip, 0);
          
        if (packet == NULL) {
            if (!curses) fprintf(stderr, "WARNING: packet build failed\n");   
            flow->error = 1;  
            continue;    
        } 
               
        rc = eth_packet_send(packet, ETH_BUF_LEN, intf);

        if (rc < 0) {
            if (!curses) fprintf(stderr, "WARNING: packet send failed: %s\n", inet_ntoa(dst)); 
            flow->error = 1;
            continue;
        }

        flow->send++;
        flow->c_send++;
        flow->error = 0;
        npacket++;
    }
}


static void 
cleanup()
{
    /*
     * Refresh here before exiting to account for any in-flight packets
     */
    refresh_tx_flow_rates();

    if (curses) { 
        usleep(10000);
        tprint(++line, 0, "Tx Stopped. Press any key to quit...");
        getch();
        endwin();
    } else {
        printf("\n\n");
    }
}

static void 
bail() 
{
   quit = 1;
}


int 
main(int argc, char *argv[]) 
{
    IF   *intf;
    int   iter = 0;
    int   rc = 0;
    int   soptindex = 1;
    char *sopt;

    signal(SIGINT, bail);

    memset(flows, 0, sizeof(flow) * MAX_FLOW);

    rc = parse_options(argc, argv, &ifname, flows, &nflows, &rate, &iter_max);

    if (rc < 0) {
        return rc;
    }

    /*
     * Initialize the interface
     */
    intf = eth_interface_init(ifname, 0);

    if (intf == NULL) {
        return -1;
    }

    if (!curses) {  
        print_tx_flows_header();
    }

    /*
     * Create the poll thread that will poll the Tx rate every second
     */
    rc = pthread_create(&poll_tx_thread, NULL, poll_tx_flows, NULL);
    
    if (rc != 0) {
        fprintf(stderr, "ERROR: Tx poll thread creation failed: %d\n", rc);
        return -1;
    } 

    if (curses) {
        initscr(); 
    }  
 
    /*
     * Main packet Tx loop. If iter_max is set to 0, then we loop forever.
     */
    while (iter_max == 0 ? !quit : iter++ < iter_max) {

        pthread_mutex_lock(&flow_mutex);

        tx_flows(intf);

        pthread_mutex_unlock(&flow_mutex);
 
        if (rate != 0) usleep(1000000/rate);
    }

    cleanup();
}


    
        
   

